package com.lee.chapter01;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;

/**
 * 对因数分解结果进行缓存的不可变容器类
 * 这个的用途还是挺多的，比如最近做的聚档的license服务，用于缓存某一个服务的license数量，就可以用这个OneValueCache
 */
public final class OneValueCache {
    private final BigInteger lastNumber;
    private final BigInteger[] lastFactors;

    public OneValueCache(BigInteger lastNumber, BigInteger[] lastFactors) {
        this.lastNumber = lastNumber;
        this.lastFactors = Arrays.copyOf(lastFactors, lastFactors.length);
    }

    public BigInteger[] getFactors(BigInteger i) {
        if (i == null || !i.equals(lastNumber)) {
            return null;
        } else {
            return Arrays.copyOf(lastFactors, lastFactors.length);
        }
    }

    public static void main(String[] args) {
        VolatileCachedFactorizer cachedFactorizer = new VolatileCachedFactorizer();
        final int batchSize = 100;
        Thread[] threads = new Thread[batchSize];
        CountDownLatch latch = new CountDownLatch(batchSize);
        for (int i = 0; i < batchSize; i++) {
            final int _i = i;
            Thread thread = new Thread(() -> {
                cachedFactorizer.service(BigInteger.valueOf(_i));
            });
            threads[i] = thread;
            latch.countDown();
        }

        // 阻塞，发令枪
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (Thread thread : threads) {
            thread.start();
        }
    }
}

class VolatileCachedFactorizer {
    // 不可变还不行，还要对多线程是可见的，所以还要加上volatile这个描述符
    private volatile OneValueCache cache = new OneValueCache(null, new BigInteger[]{});

    public void service(BigInteger i) {
        BigInteger[] factors = cache.getFactors(i);
        if (factors == null) {
            factors = factor(i);
            cache = new OneValueCache(i, factors);
        }
        System.out.printf("num:%d,factors:%s%n", i.intValue(), Arrays.toString(factors));
    }

    private BigInteger[] factor(BigInteger i) {
        return new BigInteger[]{i, BigInteger.TEN.multiply(i)};
    }
}
